package com.train.member.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.codec.Base64;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.train.member.resp.CaptchaResp;
import com.train.member.resp.LoginResp;
import com.train.member.resp.MemberSendSmsResp;
import com.train.member.resp.UserInfoResp;
import com.train.common.constants.KeyConstants;
import com.train.common.enums.impl.BizExceptionEnums;
import com.train.common.enums.impl.StatusEnums;
import com.train.common.enums.impl.SysExceptionEnums;
import com.train.common.exception.GlobalException;
import com.train.common.model.user.UserInfo;
import com.train.common.utils.captcha.CaptchaUtils;
import com.train.common.utils.date.LocalDateTimeUtils;
import com.train.common.utils.helper.UserHelper;
import com.train.common.utils.id.IdUtils;
import com.train.common.utils.io.IoUtils;
import com.train.common.utils.jwt.JwtUtils;
import com.train.common.utils.math.MathUtils;
import com.train.external.sms.entity.SmsResp;
import com.train.external.sms.service.SmsSendService;
import com.train.member.dto.MemberLoginDTO;
import com.train.member.dto.MemberSendSmsDTO;
import com.train.member.mapper.MemberMapper;
import com.train.member.po.MemberPO;
import com.train.member.service.MemberService;
import com.train.member.service.SmsRecordService;
import com.train.redis.core.support.RedisSupport;
import com.train.redis.core.utils.SerializableUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.awt.image.BufferedImage;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
* @author baitiaojun
* @description 针对表【member(会员)】的数据库操作Service实现
* @createDate 2025-02-08 17:28:33
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class MemberServiceImpl extends ServiceImpl<MemberMapper, MemberPO>
    implements MemberService {

    private final MemberMapper memberMapper;

    private final RedisSupport redisSupport;

    private final SmsRecordService smsRecordService;

    private final SmsSendService smsSendService;

    /**
     * 用户手机号注册
     * @param memberSendSmsDTO
     */
    @Transactional
    @Override
    public MemberSendSmsResp sendSms(MemberSendSmsDTO memberSendSmsDTO) {
        //校验图形验证码是否正确
        Object captcha = redisSupport.getValue(KeyConstants.CAPTCHA_CODE_KEY, memberSendSmsDTO.getCaptchaId());
        if (captcha == null) {
            throw new GlobalException(BizExceptionEnums.EXP_EXPIRE_CAPTCHA);
        }
        String captchaCode = SerializableUtils.parseObject(captcha, String.class);
        if (!captchaCode.equals(memberSendSmsDTO.getCaptchaCode())) {
            throw new GlobalException(BizExceptionEnums.EXP_ERROR_CAPTCHA);
        }

        //删除图形验证码
        redisSupport.delete(KeyConstants.CAPTCHA_CODE_KEY, memberSendSmsDTO.getCaptchaId());

        //校验手机号是否注册过,如果没有注册过保存到用户表
        String mobile = memberSendSmsDTO.getMobile();
        if (!memberMapper.existsMemberByMobile(mobile)) {
            memberMapper.addMember(mobile);
        }

        //生成随机短信验证码, 保存到redis,60秒过期
        String smsCode = MathUtils.getRandom(6);

        //校验短信发送频率(分钟级限制), 保存到短信记录表, 防止刷短信 短信记录表:[手机号、验证码、是否使用、业务类型、发送时间、使用时间]
        smsRecordService.validateSmsSendFrequency(mobile, smsCode);

        String uuid = IdUtils.fastSimpleUUID();
        redisSupport.addValue(KeyConstants.SMS_CODE_KEY, uuid, smsCode, 60L, TimeUnit.SECONDS);

        //发送短信
        SmsResp smsResp = smsSendService.sendSmsCode(mobile, smsCode);
        if (smsResp == null || !"OK".equals(smsResp.getCode())) {
            if (smsResp != null) {
                log.error("发送短信失败:{}", smsResp.getMessage());
            }
            throw new GlobalException(BizExceptionEnums.EXP_SEND_SMS_FAIL);
        }

        MemberSendSmsResp resp = new MemberSendSmsResp();
        resp.setSmsCodeId(uuid);
        return resp;
    }

    /**
     * 用户登录
     */
    @Override
    public LoginResp login(MemberLoginDTO memberLoginDTO) {
        //校验手机号是否存在
        String mobile = memberLoginDTO.getMobile();
        if (!memberMapper.existsMemberByMobile(mobile)) {
           throw new GlobalException(BizExceptionEnums.EXP_MEMBER_NOT_EXISTS);
        }

        //查询短信验证码是否过期并校验
        Object value = redisSupport.getValue(KeyConstants.SMS_CODE_KEY, memberLoginDTO.getSmsCodeId());
        if (value == null) {
            throw new GlobalException(BizExceptionEnums.EXP_EXPIRE_SMS_CODE);
        }

        //校验短信验证码是否正确
        String smsCode = SerializableUtils.parseObject(value, String.class);
        if (!smsCode.equals(memberLoginDTO.getSmsCode())) {
            throw new GlobalException(BizExceptionEnums.EXP_ERROR_SMS_CODE);
        }

        //校验短信验证码, 校验后删除验证码
        redisSupport.delete(KeyConstants.SMS_CODE_KEY, memberLoginDTO.getSmsCodeId());

        //更新短信记录表信息
        smsRecordService.editSmsRecord(mobile, smsCode, String.valueOf(StatusEnums.YES.getCode()), LocalDateTimeUtils.getNow());

        //获取用户id
        Long id = memberMapper.getIdByMobile(mobile);

        //生成JWT
        Map<String, Object> payloads = new HashMap<>();
        payloads.put("id", id);
        payloads.put("mobile", mobile);
        String token = JwtUtils.create(payloads);
        return new LoginResp(token);
    }

    /**
     * 获取图形验证码
     */
    @Override
    public CaptchaResp captcha() {
        //生成验证码
        String defaultText = CaptchaUtils.createDefaultText();
        BufferedImage captcha = CaptchaUtils.createCaptcha(defaultText);

        //验证码保存到redis
        String uuid = IdUtils.fastSimpleUUID();
        redisSupport.addValue(KeyConstants.CAPTCHA_CODE_KEY, uuid, defaultText, 1L, TimeUnit.MINUTES);

        //返回base64验证码给前端
        String captchaBase64 = Base64.encode(IoUtils.toByte(captcha));

        return new CaptchaResp(captchaBase64, uuid);
    }

    @Override
    public UserInfoResp getUserInfo() {
        UserInfo userInfoBO = UserHelper.getUserInfo();
        if (userInfoBO == null) {
            throw new GlobalException(SysExceptionEnums.EXP_TOKEN_INVALID);
        }
        return BeanUtil.copyProperties(userInfoBO, UserInfoResp.class);
    }
}




